package com.loong.android.tools;

import static java.lang.Runtime.getRuntime;

import android.os.Build;
import java.lang.Thread;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 命令行工具类
 */
public class Command {

    /**
     * 命令行数据输出的接口事件
     */
    public abstract static  class DataOutput {
        /**
         * 当命令行输出数据时触发，注意事件是在多线程触发的，多线程中不能执行UI操作
         * @param data 数据内容
         */
        public void onData(String data) {}
    }

    /**
     * 命令行错误输出的接口事件
     */
    public abstract static  class ErrorOutput {
        /**
         * 当命令行输出错误时触发，注意事件是在多线程触发的，多线程中不能执行UI操作
         * @param error 错误内容
         */
        public void onError(String error) {}
    }

    /**
     * 执行命令行，返回命令行执行的进程对象
     * @param commands 命令行数组
     * @param su 是否执行su命令，su命令需要root权限，可执行需要root权限的命令行
     * @param dataOutput 执行结构数据回调
     * @param errorOutput 执行错误回调
     */
    public static Process exec(String[] commands, boolean su, DataOutput dataOutput, ErrorOutput errorOutput) {
        if(commands == null || commands.length <= 0) return null;
        Process process = null;
        try {
             process = getRuntime().exec(su ? "su" : "sh");
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(process == null) return null;
        //同步锁
        final ReadWriteLock lock = new ReentrantReadWriteLock();
        final BufferedReader dataReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        final BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        final DataOutputStream dos = new DataOutputStream(process.getOutputStream());
        try {
            //写入要执行的命令
            for (String command : commands) {
                if (command == null) continue;
                dos.write(command.getBytes());
                dos.writeBytes("\n");
                dos.flush();
            }
            dos.writeBytes("exit\n");
            dos.flush();
            dos.close();

            //开一个线程来处理input流
            final Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    String line;
                    Lock writeLock = lock.writeLock();
                    try {
                        while ((line = dataReader.readLine()) != null) {
                            writeLock.lock();
                            if(dataOutput != null) dataOutput.onData(line);
                            writeLock.unlock();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            dataReader.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            t1.start();

            //开一个线程来处理error流
            final Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    String line;
                    Lock writeLock = lock.writeLock();
                    try {
                        while ((line = errorReader.readLine()) != null) {
                            writeLock.lock();
                            if(errorOutput != null) errorOutput.onError(line);
                            writeLock.unlock();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            errorReader.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            t2.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return process;
    }

    /**
     * 销毁命令行进程
     * @param process 进程对象
     */
    public static void processDestroy(Process process) {
        process.destroy();
    }

    /**
     * 判断命令行进程是否存活，需要android api 26版本以上系统才能使用
     * @param process 进程对象
     */
    public static boolean processIsAlive(Process process) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false;
        return process.isAlive();
    }

    /**
     * 阻塞线程等待进程，直至执行完毕。返回0表示执行正常结束，其他表示错误。
     * @param process 进程对象
     */
    public static int processWaitFor(Process process) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return -1;
        try {
            return process.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * 阻塞线程等待进程，直至执行完毕。进程正常结束返回true, 若超过限定时间为进程未终止则返回false
     * @param process 进程对象
     * @param timeout 限定超时时间，单位ms
     */
    public static boolean processWaitFor(Process process, int timeout) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false;
        try {
            return process.waitFor(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 结束进程。返回0表示进程正常终止
     * @param process 进程对象
     */
    public static int processExit(Process process) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return -1;
        try {
            return process.exitValue();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * 判断系统是否root
     */
    public static boolean isRoot() {
        String binPath = "/system/bin/su";
        String xBinPath = "/system/xbin/su";
        if (new File(binPath).exists() && isExecutable(binPath))
            return true;
        if (new File(xBinPath).exists() && isExecutable(xBinPath))
            return true;
        return false;
    }

    //检测su文件是否可执行
    private static boolean isExecutable(String filePath) {
        Process p = null;
        try {
            p = Runtime.getRuntime().exec("ls -l " + filePath);
            // 获取返回内容
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    p.getInputStream()));
            String str = in.readLine();
            if (str != null && str.length() >= 4) {
                char flag = str.charAt(3);
                if (flag == 's' || flag == 'x')
                    return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(p!=null){
                p.destroy();
            }
        }
        return false;
    }
}
